﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Media;
using OpenFrameworkCore.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Shapes;

namespace OpenFrameworkWriter.Controls
{
	/// <summary>
	/// Control을 제어하는 Editor 클래스
	/// </summary>
	[TemplatePart(Name = ROOT_ELEMENT_NAME, Type = typeof(Panel))]
	[TemplatePart(Name = WIDTH_HEIGHT_ADONER_NAME, Type = typeof(Adoner))]
	[TemplatePart(Name = WIDTH_ADONER_NAME, Type = typeof(Adoner))]
	[TemplatePart(Name = HEIGHT_ADONER_NAME, Type = typeof(Adoner))]
	[TemplatePart(Name = ANGLE_ADONER_NAME, Type = typeof(Adoner))]
	[TemplatePart(Name = CLOSE_BUTTON_NAME, Type = typeof(ButtonBase))]
	public class BasicEditor : OpenFrameworkWriter.Controls.EditorBase
	{
		/// <summary>
		/// Editor Template의 최상위 Element
		/// </summary>
		private const string ROOT_ELEMENT_NAME = "RootElement";
		private Panel RootElement;

		/// <summary>
		/// Width와 Height를 변경하는 Adoner
		/// </summary>
		private const string WIDTH_HEIGHT_ADONER_NAME = "WidthHeightAdoner";
		private Adoner WidthHeightAdoner;

		/// <summary>
		/// Width를 변경하는 Adoner
		/// </summary>
		private const string WIDTH_ADONER_NAME = "WidthAdoner";
		private Adoner WidthAdoner;

		/// <summary>
		/// Height를 변경하는 Adoner
		/// </summary>
		private const string HEIGHT_ADONER_NAME = "HeightAdoner";
		private Adoner HeightAdoner;

		/// <summary>
		/// Angle을 변경하는 Adoner
		/// </summary>
		private const string ANGLE_ADONER_NAME = "AngleAdoner";
		private Adoner AngleAdoner;

		/// <summary>
		/// CloseButton
		/// </summary>
		private const string CLOSE_BUTTON_NAME = "CloseButton";

		/// <summary>
		/// Control을 삭제할때 사용하는 버튼
		/// </summary>
		private ButtonBase CloseButton;

		/// <summary>
		/// 현재 사용 중인 TransformType
		/// </summary>
		protected TransformType currentTransformType;

		/// <summary>
		/// 마지막 마우스 위치
		/// </summary>
		protected Point lastMousePoint;

		/// <summary>
		/// 현재 Transform이 수행 중인지 나타내는 변수
		/// </summary>
		protected bool isTransforming;

		/// <summary>
		/// 현재 Template이 로드되었는지 나타내는 변수
		/// </summary>
		protected bool isTemplateLoaded;

		/// <summary>
		/// 생성자
		/// </summary>
		/// <param name="parentEditContainer">Editor가 속해있는 부모 EditContainer</param>
		public BasicEditor(BasicControlEditContainer parentEditContainer)
			: base(parentEditContainer)
		{
			DefaultStyleKey = typeof(BasicEditor);

			// Transform 초기화
			InitTransform();
		}

		/// <summary>
		/// Editor의 X좌표
		/// </summary>
		public double X
		{
			get
			{
				return (double)GetValue(XProperty);
			}

			set
			{
				SetValue(XProperty, value);
			}
		}

		/// <summary>
		/// X offset
		/// </summary>
		public static readonly DependencyProperty XProperty = DependencyProperty.Register(
			"X",
			typeof(double),
			typeof(BasicEditor),
			new PropertyMetadata(OnXPropertyChanged)
			);

		private static void OnXPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			((BasicEditor)d).OnXPropertyChanged(e);
		}

		private void OnXPropertyChanged(DependencyPropertyChangedEventArgs e)
		{
			translateTransform.X = (double)e.NewValue;
		}

		/// <summary>
		/// X 좌표 수정 가능 여부
		/// </summary>
		public bool XEditable
		{
			get
			{
				return (bool)GetValue(XEditableProperty);
			}
			set
			{
				SetValue(XEditableProperty, value);
			}
		}

		public static readonly DependencyProperty XEditableProperty = DependencyProperty.Register
			(
			"XEditable",
			typeof(bool),
			typeof(BasicEditor),
			null
			);

		/// <summary>
		/// Editor의 Y좌표
		/// </summary>
		public double Y
		{
			get
			{
				return (double)GetValue(YProperty);
			}

			set
			{
				SetValue(YProperty, value);
			}
		}

		/// <summary>
		/// Y offset
		/// </summary>
		public static readonly DependencyProperty YProperty = DependencyProperty.Register(
			"Y",
			typeof(double),
			typeof(BasicEditor),
			new PropertyMetadata(OnYPropertyChanged)
			);

		private static void OnYPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			((BasicEditor)d).OnYPropertyChanged(e);
		}

		private void OnYPropertyChanged(DependencyPropertyChangedEventArgs e)
		{
			translateTransform.Y = (double)e.NewValue;
		}

		/// <summary>
		/// Y 좌표 수정 가능 여부
		/// </summary>
		public bool YEditable
		{
			get
			{
				return (bool)GetValue(YEditableProperty);
			}
			set
			{
				SetValue(YEditableProperty, value);
			}
		}

		public static readonly DependencyProperty YEditableProperty = DependencyProperty.Register
			(
			"YEditable",
			typeof(bool),
			typeof(BasicEditor),
			null
			);

		/// <summary>
		/// Editor의 Angle
		/// </summary>
		public double Angle
		{
			get
			{
				return (double)GetValue(AngleProperty);
			}

			set
			{
				SetValue(AngleProperty, value);
			}
		}

		public static readonly DependencyProperty AngleProperty = DependencyProperty.Register(
			"Angle",
			typeof(double),
			typeof(BasicEditor),
			new PropertyMetadata(OnAnglePropertyPropertyChanged)
			);

		private static void OnAnglePropertyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			((BasicEditor)d).OnAnglePropertyPropertyChanged(e);
		}

		private void OnAnglePropertyPropertyChanged(DependencyPropertyChangedEventArgs e)
		{
			rotateTransform.Angle = (double)e.NewValue;
		}

		/// <summary>
		/// Angle 수정 가능 여부
		/// </summary>
		public bool AngleEditable
		{
			get
			{
				return (bool)GetValue(AngleEditableProperty);
			}
			set
			{
				SetValue(AngleEditableProperty, value);
			}
		}

		public static readonly DependencyProperty AngleEditableProperty = DependencyProperty.Register
			(
			"AngleEditable",
			typeof(bool),
			typeof(BasicEditor),
			new PropertyMetadata(OnAngleEditableEditablePropertyChanged)
			);

		private static void OnAngleEditableEditablePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			((BasicEditor)d).OnAngleEditableEditablePropertyChanged(e);
		}

		private void OnAngleEditableEditablePropertyChanged(DependencyPropertyChangedEventArgs e)
		{
			bool value = (bool)e.NewValue;

			if (isTemplateLoaded)
			{
				if (!value)
					AngleAdoner.Visibility = Visibility.Collapsed;
				else
					AngleAdoner.Visibility = Visibility.Visible;
			}
		}

		public static readonly DependencyProperty WidthEditableProperty = DependencyProperty.Register
			(
			"WidthEditable",
			typeof(bool),
			typeof(BasicEditor),
			new PropertyMetadata(OnWidthEditablePropertyChanged)
			);

		private static void OnWidthEditablePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			((BasicEditor)d).OnWidthEditablePropertyChanged(e);
		}

		private void OnWidthEditablePropertyChanged(DependencyPropertyChangedEventArgs e)
		{
			bool value = (bool)e.NewValue;

			if (isTemplateLoaded)
			{
				if (!value)
					WidthAdoner.Visibility = Visibility.Collapsed;
				else
					WidthAdoner.Visibility = Visibility.Visible;
			}
		}

		public static readonly DependencyProperty HeightEditableProperty = DependencyProperty.Register
			(
			"HeightEditable",
			typeof(bool),
			typeof(BasicEditor),
			new PropertyMetadata(OnHeightEditablePropertyChanged)
			);

		private static void OnHeightEditablePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			((BasicEditor)d).OnHeightEditablePropertyChanged(e);
		}

		private void OnHeightEditablePropertyChanged(DependencyPropertyChangedEventArgs e)
		{
			bool value = (bool)e.NewValue;

			if (isTemplateLoaded)
			{
				if (!value)
					HeightAdoner.Visibility = Visibility.Collapsed;
				else
					HeightAdoner.Visibility = Visibility.Visible;
			}
		}

		#region Transform Members
		/// <summary>
		/// Editor가 사용하는 TranslateTransform
		/// </summary>
		private TranslateTransform translateTransform;

		/// <summary>
		/// Editor가 사용하는 RotateTransform
		/// </summary>
		private RotateTransform rotateTransform;
		#endregion

		/// <summary>
		/// Transform을 초기화합니다.
		/// </summary>
		protected virtual void InitTransform()
		{
			// Make TransformGroup
			TransformGroup transformGroup = new TransformGroup();
			translateTransform = new TranslateTransform();
			rotateTransform = new RotateTransform();

			transformGroup.Children.Add(translateTransform);
			transformGroup.Children.Add(rotateTransform);

			// Set maked transformGroup to RenderTransform
			RenderTransform = transformGroup;
		}

		/// <summary>
		/// Transform이 발생했을때 호출되는 메서드.
		/// OnTransformRequested 메서드가 호출된후 Transformed 이벤트가 발생한다.
		/// </summary>
		/// <param name="sourceAdoner">Transform을 요구하는 Adoner</param>
		protected virtual void OnTransformRequested(Adoner sourceAdoner)
		{
		}

		/// <summary>
		/// Transform의 선택 상태가 변경되었을때 호출되는 메서드
		/// </summary>
		/// <param name="e"></param>
		protected override void OnStatePropertyChanged(DependencyPropertyChangedEventArgs e)
		{
			base.OnStatePropertyChanged(e);
		}

		/// <summary>
		/// TargetControl이 변경된 경우 필요한 요소를 바인딩한다.
		/// </summary>
		/// <param name="oldControl"></param>
		/// <param name="newControl"></param>
		protected override void OnTargetControlChanged(ControlBase oldControl, ControlBase newControl)
		{
			base.OnTargetControlChanged(oldControl, newControl);

			// 기존 Control과의 바인딩 제거
			if (oldControl != null)
				ClearBinding();

			// 새로운 Control이 들어온 경우 바인딩 초기화
			if (newControl != null)
				InitBinding();
		}

		/// <summary>
		/// Binding 제거 합니다.
		/// </summary>
		protected virtual void ClearBinding()
		{
			ClearValue(WidthProperty);
			ClearValue(HeightProperty);
			ClearValue(XProperty);
			ClearValue(YProperty);
			ClearValue(AngleProperty);
		}

		/// <summary>
		/// Binding을 초기화 합니다.
		/// </summary>
		protected virtual void InitBinding()
		{
			// Width binding 설정
			Binding widthBinding = new Binding("Width");
			widthBinding.Source = TargetControl;
			widthBinding.Mode = BindingMode.TwoWay;
			SetBinding(WidthProperty, widthBinding);

			// Height binding 설정
			Binding heightBinding = new Binding("Height");
			heightBinding.Source = TargetControl;
			heightBinding.Mode = BindingMode.TwoWay;
			SetBinding(HeightProperty, heightBinding);

			// X binding 설정
			Binding xBinding = new Binding("X");
			xBinding.Source = TargetControl;
			xBinding.Mode = BindingMode.TwoWay;
			SetBinding(XProperty, xBinding);

			// Y binding 설정
			Binding yBinding = new Binding("Y");
			yBinding.Source = TargetControl;
			yBinding.Mode = BindingMode.TwoWay;
			SetBinding(YProperty, yBinding);

			// Angle binding 설정
			Binding angleBinding = new Binding("Angle");
			angleBinding.Source = TargetControl;
			angleBinding.Mode = BindingMode.TwoWay;
			SetBinding(AngleProperty, angleBinding);

			if (TargetControl is Resizable)
			{
				// Width adoner 설정
				Binding widthAdonerBinding = new Binding("WidthEditable");
				widthAdonerBinding.Source = TargetControl;
				widthAdonerBinding.Mode = BindingMode.OneWay;
				SetBinding(WidthEditableProperty, widthAdonerBinding);

				// Height adoner 설정
				Binding heightAdonerBinding = new Binding("HeightEditable");
				heightAdonerBinding.Source = TargetControl;
				heightAdonerBinding.Mode = BindingMode.OneWay;
				SetBinding(HeightEditableProperty, heightAdonerBinding);
			}

			if (TargetControl is Rotatable)
			{
				// Angle adoner 설정
				Binding angleAdonerBinding = new Binding("AngleEditable");
				angleAdonerBinding.Source = TargetControl;
				angleAdonerBinding.Mode = BindingMode.OneWay;
				SetBinding(AngleEditableProperty, angleAdonerBinding);
			}
		}

		/// <summary>
		/// OnApplyTemplate
		/// 
		/// 템플릿이 적용된 경우 필요한 Element를 검색한다.
		/// </summary>
		public override void OnApplyTemplate()
		{
			base.OnApplyTemplate();

			// Search element by name
			RootElement = (Panel)GetTemplateChild(ROOT_ELEMENT_NAME);
			if (RootElement == null)
				throw new TemplatePartNotFoundException("RootElement를 찾을 수 없습니다.");

			// RootElement의 이벤트 처리를 위해 보이지 않는 Rectangle을 추가합니다.
			Rectangle rectangle = new Rectangle()
			{
				Fill = new SolidColorBrush(Colors.White),
				Opacity = 0,
				Width = RootElement.ActualWidth,
				Height = RootElement.ActualHeight
			};

			rectangle.SetValue(Canvas.ZIndexProperty, -1);

			RootElement.Children.Add(
				rectangle
			);

			// RootElement의 크기에 추가된 Rectangle의 크기를 맞춥니다.
			RootElement.SizeChanged += delegate(object sender, SizeChangedEventArgs e)
			{
				Panel panel = (Panel)sender;

				Width = panel.ActualWidth;
				Height = panel.ActualHeight;
			};

			// 이동을 지원하기 위해 이벤트 설치
			RootElement = (Panel)GetTemplateChild(ROOT_ELEMENT_NAME);
			RootElement.MouseMove += new System.Windows.Input.MouseEventHandler(Transforming);
			RootElement.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(EndTransform);

			WidthHeightAdoner = (Adoner)GetTemplateChild(WIDTH_HEIGHT_ADONER_NAME);
			if (WidthHeightAdoner == null)
				throw new TemplatePartNotFoundException("WidthHeightAdoner를 찾을 수 없습니다.");
            WidthHeightAdoner.MouseLeftButtonDown += new System.Windows.Input.MouseButtonEventHandler(PrepareTransform);
            WidthHeightAdoner.MouseMove += new System.Windows.Input.MouseEventHandler(Transforming);
            WidthHeightAdoner.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(EndTransform);

			WidthAdoner = (Adoner)GetTemplateChild(WIDTH_ADONER_NAME);
			if (WidthAdoner == null)
				throw new TemplatePartNotFoundException("WidthAdoner를 찾을 수 없습니다.");
            WidthAdoner.MouseLeftButtonDown += new System.Windows.Input.MouseButtonEventHandler(PrepareTransform);
            WidthAdoner.MouseMove += new System.Windows.Input.MouseEventHandler(Transforming);
            WidthAdoner.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(EndTransform);

			HeightAdoner = (Adoner)GetTemplateChild(HEIGHT_ADONER_NAME);
			if (HeightAdoner == null)
				throw new TemplatePartNotFoundException("HeightAdoner를 찾을 수 없습니다.");
            HeightAdoner.MouseLeftButtonDown += new System.Windows.Input.MouseButtonEventHandler(PrepareTransform);
            HeightAdoner.MouseMove += new System.Windows.Input.MouseEventHandler(Transforming);
            HeightAdoner.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(EndTransform);

			AngleAdoner = (Adoner)GetTemplateChild(ANGLE_ADONER_NAME);
			if (AngleAdoner == null)
				throw new TemplatePartNotFoundException("AngleAdoner를 찾을 수 없습니다.");

			CloseButton = (Button)GetTemplateChild(CLOSE_BUTTON_NAME);
			if (CloseButton == null)
				throw new TemplatePartNotFoundException("CloseButton을 찾을 수 없습니다.");
            // 삭제를 지원하기 위해 이벤트 설치
            CloseButton.Click += delegate
            {
                this.ParentControlEditContainer.
                    ControlContainerContext.
                    ControlContexts.
                    Remove((BasicControlContext)TargetControl.ControlContext);
            };

			// State에 따른 Binding 세팅
			Binding stateBinding = new Binding("State");
			stateBinding.Source = this;
			stateBinding.Mode = BindingMode.OneWay;
			stateBinding.Converter = new StateBindingConverter();

			SetBinding(OpacityProperty, stateBinding);
			WidthAdoner.SetBinding(IsEnabledProperty, stateBinding);
			HeightAdoner.SetBinding(IsEnabledProperty, stateBinding);
			WidthHeightAdoner.SetBinding(IsEnabledProperty, stateBinding);
			CloseButton.SetBinding(IsEnabledProperty, stateBinding);
			AngleAdoner.SetBinding(IsEnabledProperty, stateBinding);
		}

		/// <summary>
		/// Transform 준비
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void PrepareTransform(object sender, System.Windows.Input.MouseButtonEventArgs e)
		{
			if (e.Handled)
				return;

			e.Handled = true;

            if (sender == WidthAdoner)
                currentTransformType = TransformType.Width;
            else if (sender == HeightAdoner)
                currentTransformType = TransformType.Height;
            else if (sender == WidthHeightAdoner)
                currentTransformType = TransformType.Width | TransformType.Height;

            BeginTransform(e.GetPosition(null), currentTransformType);
		}

		/// <summary>
		/// Transform을 시작합니다.
		/// </summary>
		/// <param name="beginPoint"></param>
		/// <param name="transformType"></param>
		public void BeginTransform(Point beginPoint, TransformType transformType)
		{
			if (isTransforming)
				return;

			isTransforming = true;

			currentTransformType = transformType;

			lastMousePoint = beginPoint;

			RootElement.CaptureMouse();
		}

		/// <summary>
		/// Transform 수행
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void Transforming(object sender, System.Windows.Input.MouseEventArgs e)
		{
			if (!isTransforming)
				return;

			Point currentMousePoint = e.GetPosition(ParentControlEditContainer);

			switch (currentTransformType)
			{
				case TransformType.X | TransformType.Y:
					X += currentMousePoint.X - lastMousePoint.X;
					Y += currentMousePoint.Y - lastMousePoint.Y;

					break;

                case TransformType.Width:
                    Width += currentMousePoint.X - lastMousePoint.X;

                    break;

                case TransformType.Height:
                    Height += currentMousePoint.Y - lastMousePoint.Y;

                    break;

                case TransformType.Width | TransformType.Height:
                    Width += currentMousePoint.X - lastMousePoint.X;
                    Height += currentMousePoint.Y - lastMousePoint.Y;

                    break;

                case TransformType.Angle:

                    break;
			}

			lastMousePoint = currentMousePoint;
		}

		/// <summary>
		/// Transform 종료
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void EndTransform(object sender, System.Windows.Input.MouseButtonEventArgs e)
		{
			if (e.Handled)
				return;

			e.Handled = true;

			if (!isTransforming)
				return;

			isTransforming = false;

			lastMousePoint = new Point(0, 0);

			RootElement.ReleaseMouseCapture();
		}
	}
}
